/**
 *  Copyright 2006-2007 EmbedChat
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.embedchat.desktop.gui;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang.StringUtils;
import org.embedchat.desktop.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @author koba
 * 
 */
public class SmilesHandler {

	private static final String DEFAULT_SMILES_ARCHIVE = "default.zip";
	private static final String ICON_DEF_FILE = "icondef.xml";
	private static final Logger log = LoggerFactory.getLogger(SmilesHandler.class);

	private Map<String, String> smilesMap = new HashMap<String, String>();

	private File smilesFolder = null;

	public SmilesHandler() {
		Config config = Config.getInstance();
		if (config.getSmilesFolder() != null) {
			smilesFolder = config.getSmilesFolder();
		} else {
			smilesFolder = new File(SmilesHandler.class.getClassLoader()
					.getResource("smiles").getPath());
		}
		if (smilesFolder != null && smilesFolder.isDirectory()) {
			File zipFile = new File(smilesFolder.getPath() + File.separator + DEFAULT_SMILES_ARCHIVE);
			File targetDirectory = config.getWorkDirectory();
			File smilesWorksDir = new File(targetDirectory.getAbsolutePath() + File.separator + "smiles");
			unpackZipArchive(zipFile, smilesWorksDir);
			File iconDef = findIconDefFile(smilesWorksDir);
			buildReplacementIndex(iconDef);
		}
	}

	public void unpackZipArchive(File zipFile, File targetDirectory) {
		ZipFile zf;
		try {
			zf = new ZipFile(zipFile);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return;
		}
		for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {
			ZipEntry zipEntry = (ZipEntry) entries.nextElement();
			String zipEntryName = zipEntry.getName();
			int lastDirSep;
			if ((lastDirSep = zipEntryName.lastIndexOf('/')) > 0) {
				String dirName = zipEntryName.substring(0, lastDirSep);
				(new File(targetDirectory.getAbsolutePath() + File.separator + dirName)).mkdirs();
			}
			OutputStream out = null;
			InputStream in = null;				
			try {
				if (!zipEntryName.endsWith("/")) {
					out = new FileOutputStream(targetDirectory.getAbsolutePath() + File.separator + zipEntryName);
					in = zf.getInputStream(zipEntry);
					byte[] buf = new byte[1024];
					int len;
					while ((len = in.read(buf)) > 0) {
						out.write(buf, 0, len);
					}
				}
			} catch (IOException e) {
				log.error(e.getMessage(), e);
			} finally {
				try {
					if (in != null) {
						in.close();						
					}
				} catch (IOException e) {
					log.error(e.getMessage(), e);					
				}
				try {
					if (out != null) {
						out.close();						
					}
				} catch (IOException e) {
					log.error(e.getMessage(), e);					
				}
			}
		}
	}

	private File findIconDefFile(File smilesWorkDir) {
		File forReturn = null;
		File[] subfiles = smilesWorkDir.listFiles();
		if(subfiles.length == 1) {
			File subdir = subfiles[0];
			File[] subdirentries = subdir.listFiles();
			for (int i = 0; i < subdirentries.length; i++) {
				if (ICON_DEF_FILE.equals(subdirentries[i].getName())) {
					return subdirentries[i];
				}
			}
		}
		return forReturn;
	}
	
	private void buildReplacementIndex(File iconDef) {
		try {
			InputStream in = new FileInputStream(iconDef);
			Document doc = null;
			try {
				doc = parseFile(in);
				Node root = doc.getDocumentElement();
				NodeList nodes = root.getChildNodes();
				for (int i = 0; i < nodes.getLength(); i++) {
					if ("icon".equals(nodes.item(i).getNodeName())) {
						NodeList iconNodes = nodes.item(i).getChildNodes();
						String object = "";
						ArrayList<String> texts = new ArrayList<String>();
						for (int j = 0; j < iconNodes.getLength(); j++) {
							if ("object".equals(iconNodes.item(j)
									.getNodeName())) {
								object = iconNodes.item(j).getTextContent();
							} else if ("text".equals(iconNodes.item(j)
									.getNodeName())) {
								texts.add(iconNodes.item(j)
										.getTextContent());
							}
						}
						for (String string : texts) {
							File smilesFolder = iconDef.getParentFile();
							File itemFile = new File(smilesFolder.getAbsolutePath() + File.separator + object);
							smilesMap.put(string, itemFile.getAbsolutePath());
						}
					}
				}
			} finally {
				in.close();
			}
		} catch (IOException e) {
			smilesFolder = null;
			log.error(e.getMessage(), e);
		}
	}
		
	public Document parseFile(InputStream stream) {
		DocumentBuilder docBuilder;
		Document doc = null;
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
				.newInstance();
		docBuilderFactory.setIgnoringElementContentWhitespace(true);
		try {
			docBuilder = docBuilderFactory.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			log.error("Wrong parser configuration: ", e);
			return null;
		}
		try {
			doc = docBuilder.parse(stream);
		} catch (SAXException e) {
			log.error("Wrong XML file structure: ", e);
			return null;
		} catch (IOException e) {
			log.error("Could not read source file: ", e);			
		}
		log.debug("XML file parsed");
		return doc;
	}

	public String transformString(String original) {
		if (smilesFolder != null) {
			for (Iterator iter = smilesMap.keySet().iterator(); iter.hasNext();) {
				String element = (String) iter.next();
				original = StringUtils.replace(original, element, "<img src=\""
						+ smilesMap.get(element) + "\"></img>");
			}
		}
		return original;
	}
}
